原文地址:https://developer.android.google.cn/about/versions/nougat/android-7.0-changes
从 Android 7.0 开始,系统将阻止应用动态链接非公开 NDK 库,这种库可能会导致您的应用崩溃。此行为变更旨在为跨平台更新和不同设备提供统一的应用体验。即使您的代码可能不会链接私有库,但您的应用中的第三方静态库可能会这么做。因此,所有开发者都应进行相应检查,确保他们的应用不会在运行 Android 7.0 的设备上崩溃。如果您的应用使用原生代码,则只能使用公开 NDK API。
您的应用可通过以下三种方式尝试访问私有平台 API:
- 您的应用直接访问私有平台库。您应更新您的应用以添加该应用的库副本,或使用公开 NDK API。
- 您的应用使用一个可访问私有平台库的第三方库。即使您确定您的应用不会直接访问私有库,您仍应针对此情景测试您的应用。
- 您的应用引用一个其 APK 中未包含的库。例如,如果您尝试使用您自己的 OpenSSL 副本,但忘记将它与应用的 APK 进行捆绑,则可能会出现此情况。正常情况下,此应用可在包含
libcrypto.so
的 Android 平台版本上运行。不过,此应用在不包含此库的新版 Android(例如,Android 6.0 和更高的版本)上会崩溃。为修复此问题,请确保您的 APK 捆绑您的所有非 NDK 库。
应用不应使用 NDK 中未包含的原生库,因为这些库可能会发生更改或在不同 Android 版本之间的可用性不同。例如,从 OpenSSL 切换至 BoringSSL 即属于此类更改。此外,由于不属于 NDK 中的平台库没有兼容性要求,因此不同的设备可能提供不同级别的兼容性。
为降低此限制可能对当前发布的应用的影响,面向 API 级别 23 或更低级别的应用在 Android N 上可暂时访问颇为常用的一组库,例如 libandroid_runtime.so
、libcutils.so
、libcrypto.so
和 libssl.so
。如果您的应用加载其中某个库,logcat 会生成一个警告,并在目标设备上显示一个 Toast 来通知您。如果您看到这些警告,您应更新您的应用以添加该应用自己的库副本,或仅使用公开 NDK API。将来发布的 Android 平台可能会完全限制对私有库的使用,并导致您的应用崩溃。
所有应用在调用既非公开又不可暂时访问的 API 时都会生成一个运行时错误。结果就是 System.loadLibrary
和 dlopen(3)
同时返回 NULL
,并可能导致您的应用崩溃。您应检查应用代码以移除对私有平台 API 的使用,并使用预览版设备或模拟器全面测试应用。如果您不确定您的应用是否使用私有库,您可以检查 logcat 以识别运行时错误。
下表描述的是根据应用使用的私有原生库及其目标 API 级别 (android:targetSdkVersion
),应用预期显示的行为。
库 | 目标 API 级别 | 通过动态链接器进行运行时访问 | N Developer Preview 行为 | 最终 Android N 版本行为 | 未来的 Android 平台行为 |
---|---|---|---|---|---|
公开 NDK | 任意 | 可访问 | 合乎预期 | 合乎预期 | 合乎预期 |
私有(暂时可访问的私有库) | 23 或更低 | 暂时可访问 | 合乎预期,但您会在目标设备上收到一个 logcat 警告和一条消息。 | 合乎预期,但您会收到一个 logcat 警告。 | 运行时错误 |
私有(暂时可访问的私有库) | 24 或更高 | 受限 | 运行时错误 | 运行时错误 | 运行时错误 |
私有(其他) | 任意 | 受限 | 运行时错误 | 运行时错误 | 运行时错误 |
检查您的应用是否使用私有库
为帮助您识别加载私有库的问题,logcat 可能会生成一个警告或运行时错误。例如,如果您的应用面向 API 级别 23 或更低级别,并在运行 Android 7.0 的设备上尝试访问私有库,您可能会看到一个类似于下面所示的警告:
|
|
这些 logcat 警告通知您哪个库正在尝试访问私有平台 API,但不会导致您的应用崩溃。但是,如果应用面向 API 级别 24 或更高级别,logcat 会生成以下运行时错误,您的应用可能会崩溃:
|
|
如果您的应用使用动态链接到私有平台 API 的第三方库,您可能也会看到上述 logcat 输出。利用 Android 7.0DK 中的 readelf 工具,您可以通过运行以下命令生成给定 .so
文件的所有动态链接的共享库列表:
|
|
更新您的应用
通过下面的一些步骤,您可以修复上述类型的错误并确保您的应用不会在将来的更新版平台上崩溃:
如果您的应用使用私有平台库,您应更新它,以添加该应用自己的库副本或使用公开 NDK API。
如果您的应用使用访问私有符号的第三方库,则联系库作者以更新库。
请确保将您的所有非 NDK 库与您的 APK 打包在一起。
使用标准 JNI 函数而非来自libandroid_runtime.so的getJavaVM和getJNIEnv:
123AndroidRuntime::getJavaVM -> GetJavaVM from <jni.h>AndroidRuntime::getJNIEnv -> JavaVM::GetEnv orJavaVM::AttachCurrentThread from <jni.h>.
使用__system_property_get而非来自libcutils.so的私有property_get符号。为此,请使用
__system_property_get及以下 include 函数:
1#include <sys/system_properties.h>
注:系统属性的可用性和内容未通过 CTS 进行测试。应执行进一步修复以避免同时使用这些属性。
- 使用来自
libcrypto.so
的SSL_ctrl
符号的本地版本。例如,您应在您的.so
文件中静态链接libcyrpto.a
,或从 BoringSSL/OpenSSL 添加一个动态链接的libcrypto.so
版本,并将其打包到您的 APK 中。